interface TreeItemData {
	id?: number;
	content: string;
	caption?: string;
	param?: string | number;
	foldericon?: string;
	datas?: any;

	key?: string;
	value?: any;
	lock?: boolean;
	hide?: boolean;

	__option?: TreeOption;
	__tree?: TreeView;
	__item?: TreeViewItem;

	items?: TreeItemData[];
	type?: number;
	selected?: boolean;
}

type fillterFuncion = (i: TreeItemData) => boolean;
type TreeFilterType = string | fillterFuncion;

interface TreeOption {
	arrowicon: string;
	foldericon: { normal: string; press: string };
	fileicon: string;
	selectedColor: number;
	bgColor: number;
	itemHeight: number;
	fontSize: number;
	tab: number;
	drag: boolean;

	filter?: TreeFilterType;
	useLock?: boolean;
	useEye?: boolean;
}

class TreeView extends eui.Component implements eui.UIComponent {
	_name = 'tree';

	/**
	 *  Scroller Control
	 */
	Scroller: eui.Scroller;

	/**
	 * 	DataGroup
	 */
	DataGroup: TreeDataGroup;

	/**
	 * items: treeView data
	 */
	items: TreeItemData[] = [];

	static clearSelectEvent = 'ENV_TREE_CLEARSELECT';

	static selectEvent = 'ENV_TREE_SELECTITEM';

	static eyeEvent = 'ENV_TREEITEM_EYE';

	static lockEvent = 'ENV_TREEITEM_LOCK';

	// static saveEvent = "ENV_TREE_SAVE";
	static tapEvent = 'ENV_TREEITEM_TAP';

	static spaceEvent = 'ENV_TREEITEM_SPACE';

	static enterEvent = 'ENV_TREEITEM_ENTER';

	static altenterEvent = 'ENV_TREEITEM_NUMENTER';

	// 不像mouse可以全局检测shift，所以只能添加Event ShiftEnter!
	static shiftenterEvent = 'ENV_TREEITEM_SHIFTENTER';

	static deleteEvent = 'ENV_TREEITEM_DELETE';

	private itemDataCollection: eui.ArrayCollection;

	defaultOption(): void {
		const opt = {
			arrowicon: 'dtriangle_png',
			foldericon: {
				normal: 'folder_normal_png',
				press: 'folder_press_png',
			},
			fileicon: 'picture_png',
			selectedColor: 0xb8aaaa,
			bgColor: 0xffffff,
			itemHeight: 40,
			fontSize: 20,
			tab: 50,
			drag: this.itemDrag,
		};
		this.setOption(opt);
	}

	setOption(opt: TreeOption): void {
		this.option = opt;
		if (this.rect) {
			this.rect.fillColor = opt.bgColor;
		}
	}

	constructorRoot(name: string, data: any[]): void {
		this.constructorArray([{ id: 0, content: name, items: data }]);
	}

	private expandNextFrame(): void {
		const root = this.getFirst();
		if (root) {
			if (!root.isExpand()) {
				this.selectItem(root);
				root.expand(true);
			}
		}
	}

	expandFirst(): void {
		// calllater不好使，必须nextFrame
		nextFrame(this.expandNextFrame, this);
	}

	getRoot(): TreeViewItem {
		return this.getFirst();
	}

	getFirst(): TreeViewItem {
		if (this.DataGroup && this.DataGroup.numChildren > 0) {
			// let obj = (this.DataGroup.getElementAt(0));
			const obj = this.DataGroup.getChildAt(0);
			if (obj instanceof TreeViewItem) {
				return obj;
			}
		}
		return null;
	}

	constructorName(name: string): void {
		const data = [name, []];
		this.constructorStrings(data);
	}

	// data = ["xxx",["xxx",[],]]
	constructorStrings(data: any[]): void {
		function array2item(da: any[], index: number): any {
			const d2 = [];
			let prev;
			for (const d of da) {
				if (typeof d === 'string') {
					// eslint-disable-next-line no-param-reassign
					prev = { id: index++, content: d };
					d2.push(prev);
				} else if (Array.isArray(d)) {
					if (prev) {
						prev.items = array2item(d, index);
					}
				}
			}
			return d2;
		}
		const out = array2item(data, 0);
		this.constructorArray(out);
	}

	option: TreeOption;

	constructorArray(data: any[]): void {
		// let items = Utils.deepCopy(data)
		const items = data;

		// eslint-disable-next-line @typescript-eslint/no-this-alias
		const self = this;
		function fillopt(objs: any[]): void {
			for (let i = 0; i < objs.length; i++) {
				const obj = objs[i];
				obj.__option = self.option;
				obj.__tree = self;
				if (obj.items) {
					fillopt(obj.items);
				}
			}
		}

		fillopt(items);

		this.items = items;
		this.updateItems();
	}

	getJsonData(): any {
		const item = this.getFirst();
		return item.getJsonData();
	}

	getData(): any {
		const item = this.getFirst();
		return item.getData();
	}

	/* data example:
	{
		"drama": [
		  {
			"顺序执行": [
			  {
				"同时执行": [
				  {"content": "guide"},
				  {"content": "guide"},
				]
			  },
			  {"content": "音乐"},
			]
		  },
		  {"content": "界面"},
		]
	  }
	*/
	// 把array当做items来使用！
	constructorJson(
		jname: string,
		data: any,
		getIcon: (obj: any) => string,
		getParam: (obj: any) => any,
	): boolean {
		function obj2items(obj: any, name: string, id: number): any {
			if (Array.isArray(obj)) {
				const item = { id: id++, content: name, items: [] };
				let key = 0;
				for (const v of obj) {
					item.items.push(obj2items(v, String(key), id++));
					key++;
				}
				return item;
			}
			if (typeof obj === 'object') {
				const [k, v] = miscUtils.getArrayofobj(obj);
				if (!k) {
					const foldericon = getIcon && getIcon(obj);
					const param = getParam && getParam(obj);
					return {
						id: id++,
						content: `${obj.content}`,
						param,
						foldericon,
						datas: obj,
					};
				}

				return obj2items(v, String(k), id++);
			}
			egret.assert(false);
			return null;
		}

		const [k, v] = miscUtils.getArrayofobj(data);
		if (!k) {
			return false;
		}

		const out = obj2items(v, String(k), 0);
		this.constructorArray([out]);
		return true;
	}

	clearTree(): void {
		this.items = [];
		this.updateItems();
	}

	updateItems(): void {
		// console.log("TreeView.updateItems()")
		if (this.created) {
			this.itemDataCollection = new eui.ArrayCollection(this.items);
			this.DataGroup.dataProvider = this.itemDataCollection;
		}
	}

	rect: eui.Rect;

	created = false;

	protected override childrenCreated(): void {
		this.created = true;

		const rect = new eui.Rect();
		rect.fillColor = 0xffffff;
		fullAlign(rect);
		this.addChild(rect);
		this.rect = rect;

		// super.childrenCreated();
		this.Scroller = new eui.Scroller();
		this.addChild(this.Scroller);
		this.Scroller.x = 0;
		this.Scroller.y = 0;
		this.Scroller.scrollPolicyH = 'off';

		fullAlign(this.Scroller);

		this.DataGroup = new TreeDataGroup();
		this.DataGroup.renderw = this.width;
		this.DataGroup.renderh = 40;
		this.DataGroup.useVirtualLayout = false; // 不对显示优化了

		this.DataGroup.y = this.height;
		this.DataGroup.x = 0;
		this.addChild(this.DataGroup);
		this.DataGroup.left = 0;
		this.DataGroup.right = 0;

		// 2.设置相关参数
		this.updateItems();

		this.DataGroup.itemRenderer = TreeViewItem;
		this.Scroller.viewport = this.DataGroup;
		// 大数据优化:numElements 会获得总的数据条数. numChildren 会获得具体的实例数量.

		this.addEventListener(TreeView.clearSelectEvent, this.clearSelect, this);
	}

	clearSelect(): void {
		// console.log("clearSelect")
		function clearTreeItem(objs: egret.DisplayObjectContainer): void {
			for (const c of objs.$children) {
				if (c instanceof TreeViewItem) {
					c.setSelected(false);
				}
				if (c instanceof egret.DisplayObjectContainer) {
					clearTreeItem(c);
				}
			}
		}
		clearTreeItem(this);
	}

	public getSelectedItem(): TreeViewItem {
		function getTreeItem(objs: egret.DisplayObjectContainer): TreeViewItem {
			for (const c of objs.$children) {
				if (c instanceof TreeViewItem) {
					if (c.isSelected()) {
						return c;
					}
				}
				if (c instanceof egret.DisplayObjectContainer) {
					const rt = getTreeItem(c);
					if (rt) {
						return rt;
					}
				}
			}
			return null;
		}
		return getTreeItem(this);
	}

	selectItemNotClick(item: TreeViewItem): void {
		this.selectItem(item);
		this.scrollToItem(item);
	}

	selectItem(item: TreeViewItem): void {
		this.clearSelect();
		this.doSelect(item);
	}

	doSelect(item: TreeViewItem): void {
		item.setSelected(true);
		this.dispatchEventWith(TreeView.selectEvent, false, item);
	}

	// 这个仅仅包括所有创建出来的节点，尚有很多未创建的节点
	allItems(): TreeViewItem[] {
		const list = [];
		function fillItem(objs: egret.DisplayObjectContainer): void {
			for (const c of objs.$children) {
				if (c instanceof TreeViewItem) {
					list.push(c);
				}
				if (c instanceof egret.DisplayObjectContainer) {
					fillItem(c);
				}
			}
		}
		fillItem(this);
		return list;
	}

	// findDataById(id:number){
	// 	function finditems(items:any[],id:number){
	// 		for (let it of items){
	// 			if (it.id == id)
	// 				return it
	// 			if (it.items){
	// 				let i = finditems(it.items,id)
	// 				if (i)
	// 					return i
	// 			}
	// 		}
	// 	}
	// 	return finditems(this.items,id)
	// }

	findDataByContent(content1: string): any {
		function finditems(items: any[], content: string): any {
			for (const it of items) {
				if (it.content === content) {
					return it;
				}
				if (it.items) {
					const i = finditems(it.items, content);
					if (i) {
						return i;
					}
				}
			}
			return null;
		}
		return finditems(this.items, content1);
	}

	deleteId(id: number): boolean {
		function delitems(items: any[], id2: number): any {
			if (items.removeIf((a) => a.id === id2)) {
				return true;
			}
			for (const it of items) {
				if (it.items) {
					const i = delitems(it.items, id2);
					if (i) {
						return i;
					}
				}
			}
			return null;
		}
		const rt = delitems(this.items, id);
		if (rt) {
			this.updateItems();
		}
		return rt;
	}

	findId(id: number): TreeViewItem {
		const l = this.allItems();
		for (const item of l) {
			if (item.data.id === id) {
				return item;
			}
		}
		return null;
	}

	findData(data: any): TreeViewItem {
		const l = this.allItems();
		for (const item of l) {
			if (item.data === data) {
				return item;
			}
		}
		return null;
	}

	findContent(content: string): TreeViewItem {
		const l = this.allItems();
		for (const item of l) {
			if (item.getContent() === content) {
				return item;
			}
		}
		return null;
	}

	findItemByKeys(key: string): TreeViewItem {
		const first = this.getRoot();
		if (first) {
			return first.findItemByKeys(key);
		}
		return null;
	}

	findContentLike(content: string): TreeViewItem {
		const l = this.allItems();
		for (const item of l) {
			if (item.getContent().indexOf(content) === 0) {
				return item;
			}
		}
		return null;
	}

	getNextItem(item: TreeViewItem, next: boolean): TreeViewItem {
		const l = this.allItems();
		let pos = l.indexOf(item);
		if (pos < 0) {
			return null;
		}
		if (next) {
			pos++;
		} else {
			pos--;
		}
		return l[pos];
	}

	moveItem(item: TreeViewItem, next: boolean): void {
		const it = this.getNextItem(item, next);
		if (!it) {
			return;
		}
		this.selectItem(it);
		this.scrollToItem(it);
	}

	// 滚动，让他可见！
	scrollToItem(item: TreeViewItem): void {
		const p1 = item.localToGlobal();
		const p2 = this.localToGlobal();
		const old = this.Scroller.viewport.scrollV;
		const end = p2.y + this.height;
		if (p1.y + item.myHeight > end) {
			this.Scroller.viewport.scrollV += p1.y + item.myHeight - end;
		} else if (p1.y < p2.y) {
			this.Scroller.viewport.scrollV += p1.y - p2.y;
		}
		// console.log("scrollToItem",p1,this.height,old,this.Scroller.viewport.scrollV)
	}

	moveUP(item: TreeViewItem, ev: KeyboardEvent): void {
		if (this.itemDrag && ev.ctrlKey) {
			// this.indexItem(item,false)
			item.indexAdd(false);
			return;
		}
		this.moveItem(item, false);
	}

	moveDown(item: TreeViewItem, ev: KeyboardEvent): void {
		if (this.itemDrag && ev.ctrlKey) {
			item.indexAdd(true);
			return;
		}
		this.moveItem(item, true);
	}

	onKey(ev: KeyboardEvent, status: string): boolean {
		// console.log("TreeView.onKey",ev,status)
		if (status !== 'down') {
			return false;
		}
		const cur = this.getSelectedItem();
		if (!cur) {
			return false;
		}
		return this.onKeyDown(cur, ev);
	}

	onKeyDown(cur: TreeViewItem, ev: KeyboardEvent): boolean {
		// left right expand and unexpand
		// down and up move the select
		const { code } = ev;
		switch (code) {
			case 'ArrowRight':
				if (cur.isFolder() && !cur.isExpand()) {
					cur.expand(true);
				}
				break;
			case 'ArrowLeft':
				if (cur.isFolder() && cur.isExpand()) {
					cur.unexpand();
				}
				break;
			case 'ArrowUp':
				this.moveUP(cur, ev);
				break;
			case 'ArrowDown':
				this.moveDown(cur, ev);
				break;
			case 'Space':
				this.dispatchEventWith(TreeView.spaceEvent, true, cur);
				break;
			case 'Enter':
			case 'NumpadEnter':
				this.dispatchEventWith(
					ev.shiftKey
						? TreeView.shiftenterEvent
						: ev.altKey
						? TreeView.altenterEvent
						: TreeView.enterEvent,
					true,
					cur,
				);
				break;
			case 'Delete':
				this.dispatchEventWith(TreeView.deleteEvent, true, cur);
				break;
			default:
		}

		return true;
	}

	DelItem(item: TreeViewItem): void {}

	ChangeItem(item: TreeViewItem, id: number): void {}

	initEyeLock(): void {
		nextFrame(() => this._initEyeLock(), null);
	}

	_initEyeLock(): void {}

	// enableRootSave(){
	// 	this.items[0].save = true
	// }

	filterItem(filter: TreeFilterType): void {
		this.option.filter = filter;
		this.updateItems();
	}

	itemDrag = false;

	onHoldEnd(): void {
		this.Scroller.scrollPolicyV = eui.ScrollPolicy.ON;
		// this.Scroller.viewport = null;
		eui.Scroller.scrollThreshold = 5;
	}

	onHoldStart(): void {
		this.Scroller.scrollPolicyV = eui.ScrollPolicy.OFF;
		// this.Scroller.viewport = this.DataGroup;
		eui.Scroller.scrollThreshold = 5000;
		// 因为scroll的bug，导致只能用scrollThreshold + [12]来实现禁止移动！
		this.Scroller.$Scroller[12] = false;
	}

	// 供attrwindow回调
	onShowAttr(item: TreeViewItem): any {
		return null;
	}

	onAttrEnter(titem: TreeViewItem, item: AttrItem): void {}

	updateItemJson(item: TreeViewItem, value: string): void {}

	// 进入和离开
	leaveTree(): void {}

	enterTree(): void {}

	getLeaveToRoot(leave: TreeViewItem): string[] {
		let f = leave.getParentItem();
		const paths = [];
		paths.push(leave.getText());
		while (f) {
			paths.push(f.getText());
			f = f.getParentItem();
		}
		return paths;
	}

	async expandAndSelect(path: string[]) {
		let index = 1;
		let start = this.getRoot();
		while (start && path[index]) {
			const item = start.getChildItem(path[index]);
			index++;
			if (item) {
				if (item.isFolder()) {
					item.expand(true);
					start = item;
					await waitMs(100);
				} else {
					this.selectItem(item);
					return;
				}
			}
		}
	}

	run() {
		const cur = this.getSelectedItem();
		if (cur) {
			this.dispatchEventWith(TreeView.enterEvent, true, cur);
		}
	}
}
